package authservice

import (
	"fmt"
	"strings"
	"yunj/pkg/global"
	"yunj/pkg/yunj/app/data"
	"yunj/pkg/yunj/app/enum/admin/authenum"
	"yunj/pkg/yunj/app/model"
	"yunj/pkg/yunj/app/service/admin/adminservice"
	"yunj/pkg/yunj/util"

	"github.com/gin-gonic/gin"
)

type auth struct {
}

var Auth = &auth{}

// 获取当前登录成员的菜单权限keys
func (s *auth) GetMenuAuths(ctx *gin.Context, authType *authenum.Type) (auths []*model.AdminAuth, err error) {
	member := adminservice.GetLoginMember(ctx)
	if member == nil || member.Id <= 0 {
		err = fmt.Errorf("获取当前登录成员失败！")
		return
	}
	// 初始化拥有的菜单权限
	member.InitOwnMenuAuths(data.Auth.GetAllMenuAuths(), data.Auth.GetAllAuthKeys())
	switch authType.Value {
	case authenum.TYPE_SIDEBAR_MENU:
		auths = member.OwnSidebarMenuAuths
	case authenum.TYPE_TOP_MENU:
		auths = member.OwnTopMenuAuths
	default:
		err = fmt.Errorf("获取菜单权限失败！类型[%v]不存在", authType.Value)
	}
	return
}

// 生成菜单权限a标签的onclick脚本
func (s *auth) generateMenuAuthAtagOnclickScript(auth *model.AdminAuth) (script string) {
	if auth.RequestUrl == "" {
		return
	}
	// 获取请求必须参数
	urlVars := map[string]any{}
	if len(auth.RequestRequireParams) > 0 {
		for _, requestRequireParam := range auth.RequestRequireParams {
			if keyRaw, exists := requestRequireParam["key"]; exists {
				if key, _ := util.ToString(keyRaw); key != "" {
					if valueRaw, exists := requestRequireParam["value"]; exists {
						if value, _ := util.ToString(valueRaw); value != "" {
							urlVars[key] = value
						}
					}
				}
			}
		}
	}
	// 拼接url
	url := util.BuildUrl(string(auth.RequestUrl), urlVars)

	switch auth.PageOpen {
	case authenum.PAGE_OPEN_TAB:
		script = fmt.Sprintf("yunj.openTab('%s','%s')", url, auth.Name)
	case authenum.PAGE_OPEN_POPUP:
		script = fmt.Sprintf("yunj.openPopup('%s','%s')", url, auth.Name)
	case authenum.PAGE_OPEN_NEW:
		script = fmt.Sprintf("yunj.openNewPage('%s')", url)
	}
	if script != "" {
		script = fmt.Sprintf("onclick=%s", script)
	}
	return
}

// 生成菜单权限html
func (s *auth) generateMenuAuthHtml(auth *model.AdminAuth) (html string) {
	onclickScript := s.generateMenuAuthAtagOnclickScript(auth)
	switch auth.Type {
	case authenum.TYPE_TOP_MENU:
		html = auth.Name
		if iconClass := util.IconClass(auth.Icon); iconClass != "" {
			html = fmt.Sprintf("<i class='%s'></i>%s", iconClass, html)
		}
		html = fmt.Sprintf("<span class='desc'>%s</span>", html)
		if len(auth.SubMenuAuths) > 0 {
			subIconClass := "layui-icon-right"
			if auth.ParentKey == "" {
				subIconClass = "layui-icon-down"
			}
			html += fmt.Sprintf("<i class='layui-icon %s'></i>", subIconClass)
		}
		html = fmt.Sprintf("<a href='javascript:void(0);' %s title='%s'>%s</a>", onclickScript, auth.Name, html)
	case authenum.TYPE_SIDEBAR_MENU:
		html = fmt.Sprintf("<cite>%s</cite>", auth.Name)
		if iconClass := util.IconClass(auth.Icon); iconClass != "" {
			html = fmt.Sprintf("<i class='%s left-nav-li' lay-tips='%s'></i>%s", iconClass, auth.Name, html)
		}
		if len(auth.SubMenuAuths) > 0 {
			html += "<i class='layui-icon layui-icon-left li-icon-right'></i>"
		}
		atagStyle := fmt.Sprintf("style='padding-left:%vpx;'", 15*auth.MenuLevel)
		html = fmt.Sprintf("<a href='javascript:void(0);' %s %s>%s</a>", atagStyle, onclickScript, html)
	}
	return
}

// 获取当前登录成员的菜单html
func (s *auth) GetMenuHtml(ctx *gin.Context, authType *authenum.Type, auths ...[]*model.AdminAuth) (html string, err error) {
	html = ""
	var menuAuths []*model.AdminAuth
	if len(auths) > 0 {
		menuAuths = auths[0]
	} else {
		menuAuths, err = s.GetMenuAuths(ctx, authType)
		if err != nil {
			return
		}
	}
	for _, auth := range menuAuths {
		authHtml := s.generateMenuAuthHtml(auth)
		if len(auth.SubMenuAuths) > 0 {
			var subAuthHtml string
			subAuthHtml, err = s.GetMenuHtml(ctx, authType, auth.SubMenuAuths)
			if err != nil {
				return
			}
			if subAuthHtml != "" {
				authHtml += authType.GetSubMenuLayout(subAuthHtml)
			}
		}
		html += authType.GetMenuLayout(authHtml)
	}
	return
}

// 获取当前面包屑html
func (s *auth) GetBreadcrumbHtml(ctx *gin.Context) (html string, err error) {
	html = ""
	if !strings.EqualFold(ctx.Request.Method, "GET") || util.IsAsync(ctx) {
		return
	}
	if strings.Contains(ctx.Request.RequestURI, global.Config.App.Admin.DashboardUrl) {
		html = "<a href='javascript:void(0);'><cite>我的桌面</cite></a>"
		return
	}
	auths := []*model.AdminAuth{}
	auth := adminservice.GetRequestAuth(ctx)
	if auth == nil || auth.Key == "" {
		return
	}
	err = s.handleBreadcrumbPathAuths(ctx, &auths, auth)
	if err != nil {
		return
	}
	for idx, auth := range auths {
		itemHtml := auth.Name
		isLast := idx == (len(auths) - 1) // 是否是最后一个
		if isLast {
			itemHtml = fmt.Sprintf("<cite>%s</cite>", itemHtml)
		}
		onclickScript := ""
		if !isLast {
			onclickScript = s.generateMenuAuthAtagOnclickScript(auth)
		}
		itemHtml = fmt.Sprintf("<a href='javascript:void(0);' %s>%s</a>", onclickScript, itemHtml)
		html += itemHtml
	}
	return
}

// 处理当前面包屑路径权限
func (s *auth) handleBreadcrumbPathAuths(ctx *gin.Context, auths *[]*model.AdminAuth, parentAuth *model.AdminAuth) (err error) {
	if parentAuth == nil || parentAuth.Key == "" {
		return
	}
	// 菜单或内部页面
	parentAuthType, ok := authenum.TypeEnum.IsValue(parentAuth.Type)
	if ok && authenum.TypeByValue(parentAuthType).IsMenu() || (func() bool {
		parentAuthPageOpen, ok := authenum.PageOpenEnum.IsValue(parentAuth.PageOpen)
		return ok && authenum.PageOpenByValue(parentAuthPageOpen).IsInnerPage()
	})() {
		*auths = util.SlicePrepend(*auths, parentAuth)
	}
	if parentAuth.ParentKey != "" {
		pAuth := data.Auth.GetAuthByKey(parentAuth.ParentKey)
		err = s.handleBreadcrumbPathAuths(ctx, auths, pAuth)
		if err != nil {
			return
		}
	}

	return
}

// 获取全局搜索结果html
func (s *auth) GetGlobalSearchResultHtml(ctx *gin.Context) (html string, err error) {
	html = ""
	member := adminservice.GetLoginMember(ctx)
	if member == nil || member.Id <= 0 {
		err = fmt.Errorf("获取当前登录成员失败！")
		return
	}
	// 初始化拥有的权限
	member.InitOwnAuths(data.Auth.GetAllAuths(), data.Auth.GetAllAuthKeys())
	for _, auth := range member.OwnAuths {
		onclickScript := s.generateMenuAuthAtagOnclickScript(auth)
		if onclickScript == "" {
			continue
		}
		html += fmt.Sprintf("<div class='item' data-key='%s' data-desc='%s'><a href='javascript:void(0);' %s title='%s'>%s</a></div>", auth.Key, auth.FullName, onclickScript, auth.FullName, auth.FullName)
	}
	return
}

// 获取当前请求权限
func (s *auth) GetRequestAuth(ctx *gin.Context) (auth *model.AdminAuth, err error) {
	requestUrlPath := ctx.Request.URL.Path // 不带参数的url路径
	requestMethod := ctx.Request.Method
	requestQueryParams := util.QueryParams(ctx) // url携带的参数
	requestParams, err := util.GetAllParams(ctx)
	if err != nil {
		return
	}
	// fmt.Printf("当前请求%v的所有参数：%v\r\n", requestUri, requestParams)
	maxMatch := 0 // 最大匹配指数
	for _, authItem := range data.Auth.GetAllAuths() {
		// 匹配请求地址
		if !requestAuthMatchUrl(requestUrlPath, &requestQueryParams, authItem) {
			continue
		}
		match := 0 // 初始匹配指数0
		res := requestAuthMatch(&match, requestMethod, requestParams, authItem)
		if res && match > maxMatch {
			maxMatch = match
			auth = authItem
		}
	}
	return
}

// 请求权限地址匹配
// requestUrlPath 不带参数的请求地址
// requestQueryParams 请求地址携带的参数
// auth 要匹配的权限数据
// return bool 是否匹配
func requestAuthMatchUrl(requestUrlPath string, requestQueryParams *map[string]any, auth *model.AdminAuth) bool {
	// 先匹配不带参数的请求地址
	if requestUrlPath != auth.GetRequestUrlPath() {
		return false
	}
	// 获取权限地址配置携带的参数
	authRequestUrlParams := auth.GetRequestUrlParams()
	if authRequestUrlParams != nil && len(*authRequestUrlParams) > 0 {
		for k, v := range *authRequestUrlParams {
			existValue, exists := (*requestQueryParams)[k]
			if !exists || existValue != v {
				return false
			}
		}
	}
	return true
}

// 请求权限匹配
// match 匹配指数
// requestMethod 请求method
// requestParams 请求参数
// auth 要匹配的权限数据
// return bool 是否匹配
func requestAuthMatch(match *int, requestMethod string, requestParams map[string]any, auth *model.AdminAuth) bool {
	// 初始设置不通过
	res := false
	for _, fun := range requestAuthMatchFuncs {
		res = fun(match, requestMethod, requestParams, auth)
		if !res {
			// 权限匹配失败
			break
		}
	}
	return res
}

// 请求权限的匹配方法
var requestAuthMatchFuncs = []func(match *int, requestMethod string, requestParams map[string]any, auth *model.AdminAuth) bool{
	// 校验请求method
	func(match *int, requestMethod string, requestParams map[string]any, auth *model.AdminAuth) bool {
		if auth.RequestMethod == "" {
			// 没有配置method表示都可以接受
			*match = *match + 1
			return true
		}
		if strings.EqualFold(auth.RequestMethod, requestMethod) {
			// 有配置method，且配置校验通过，则匹配度比没有配置的高1
			*match = *match + 2
			return true
		}
		return false
	},
	// 校验请求必须的参数
	func(match *int, requestMethod string, requestParams map[string]any, auth *model.AdminAuth) bool {
		// fmt.Printf("权限%s要求的必须参数：%v\r\n", auth.Key, auth.RequestRequireParams)
		if len(auth.RequestRequireParams) <= 0 {
			// 没有配置request_require_params表示都可以接受
			*match = *match + 1
			return true
		}
		_match := 0
		for _, param := range auth.RequestRequireParams {
			keyRaw, exists := param["key"]
			if !exists {
				continue
			}
			key, ok := keyRaw.(string)
			if !ok || key == "" {
				continue
			}
			requestParamValueRaw, exists := requestParams[key]
			if !exists {
				return false
			}
			if valueRaw, exists := param["value"]; exists {
				if value, ok := valueRaw.(string); ok && value != "" {
					requestParamValue, err := util.ToString(requestParamValueRaw)
					if err != nil || requestParamValue != value {
						return false
					}
				}
			}
			_match = _match + 1
		}
		if _match > 0 {
			// 有配置request_require_params，且配置校验通过，则匹配度比没有配置的高1
			_match = _match + 1
		}
		*match = *match + _match
		return true
	},
}
